import { type Node, type Edge } from '@xyflow/react';
import { type BehaviorTreeNode, NodeType, type NodeData } from '../types/behaviorTree';

/**
 * @interface HierarchicalNode
 * @description 定义行为树层级化结构中的节点接口。
 * @property {string} id - 节点的唯一标识符。
 * @property {NodeType | string} type - 节点的类型（例如 'sequence', 'action', 'condition', 'selector', 'decorator'）。
 * @property {string} name - 节点的显示名称，用于在UI中展示。
 * @property {Record<string, unknown>} [properties] - 节点的自定义属性，例如动作节点的具体参数、条件节点的判断条件等。
 * @property {HierarchicalNode[]} [children] - 子节点数组。仅复合节点（如 Sequence, Selector）和装饰节点拥有此属性。
 */
export interface HierarchicalNode {
    id: string;
    type: NodeType | string;
    name: string;
    properties?: Record<string, unknown>;
    children?: HierarchicalNode[];
}

/**
 * @function exportToHierarchyJSON
 * @description 将 React Flow 的扁平化节点和边数据转换为层级化的行为树 JSON 对象。
 *              此函数用于将编辑器中的可视化行为树结构转换为可导出、存储或与其他系统交互的标准JSON格式。
 * @param {Node<NodeData>[]} nodes - React Flow 的节点数组，包含所有节点的数据和位置信息。
 * @param {Edge[]} edges - React Flow 的边数组，定义了节点之间的连接关系。
 * @returns {HierarchicalNode} 表示整个行为树的层级化 JSON 对象，其中包含根节点及其所有子节点的递归结构。
 * @throws {Error} 如果行为树结构不符合导出规范，例如：
 *                 - 行为树中没有节点。
 *                 - 行为树中没有根节点 (即所有节点都有入边)。
 *                 - 行为树中存在多个根节点。
 *                 - 在构建层级结构时，找不到某个节点ID对应的节点 (表示数据不一致)。
 */
export function exportToHierarchyJSON(nodes: Node<NodeData>[], edges: Edge[]): HierarchicalNode {
  if (nodes.length === 0) {
    throw new Error('导出失败: 行为树中没有节点。'); // 当没有节点时，抛出明确错误
  }

  const nodeMap = new Map<string, Node<NodeData>>();
  nodes.forEach((node) => nodeMap.set(node.id, node));

  // 找到所有作为目标节点的ID (即有父节点的节点)
  const childNodeIds = new Set(edges.map((edge) => edge.target));

  // 根节点是没有父节点的节点
  const rootNodes = nodes.filter((node) => !childNodeIds.has(node.id));

  if (rootNodes.length === 0) {
    throw new Error('导出失败: 行为树中没有根节点。请确保至少有一个节点没有父节点。');
  }
  if (rootNodes.length > 1) {
    throw new Error('导出失败: 行为树中存在多个根节点。请确保有且仅有一个根节点。');
  }

  const rootNode = rootNodes[0];

  /**
   * @function buildHierarchy
   * @description 递归构建层级化行为树结构。
   * @param {string} nodeId - 当前需要构建的节点 ID。
   * @returns {HierarchicalNode} 构建好的层级化节点对象，包含其子节点（如果存在）。
   * @throws {Error} 如果在构建过程中找不到节点ID对应的节点。
   */
  const buildHierarchy = (nodeId: string): HierarchicalNode => {
    const node = nodeMap.get(nodeId);
    if (!node) {
      throw new Error(`节点ID为 ${nodeId} 的节点未找到。`);
    }

    const hierarchicalNode: HierarchicalNode = {
      id: node.id,
      type: node.type || NodeType.Action, // 如果type缺失，默认为Action
      name: node.data.label || 'Unnamed Node', // 节点名称，如果data.label缺失，则默认为'Unnamed Node'
    };

    // 提取节点的自定义属性，排除 React Flow 内部属性（如 'position', 'xPos', 'yPos', 'positionAbsolute'）和 'label'。
    // 导出的 JSON 应该包含所有非内部属性，即使它们是 NaN、null 或空字符串，因为这些可能是有意义的状态。
    const properties: Record<string, unknown> = {};
    for (const key in node.data) {
      // 确保是自身的属性，而不是原型链上的属性，并且不是 React Flow 内部属性
      if (
        Object.prototype.hasOwnProperty.call(node.data, key) &&
        key !== 'label' &&
        key !== 'position' &&
        key !== 'positionAbsolute' &&
        key !== 'xPos' &&
        key !== 'yPos'
      ) {
        const value = node.data[key];
        // 不再排除 NaN、null 和空字符串，因为这些也可能是需要导出的有效数据
        properties[key] = value;
      }
    }

    if (Object.keys(properties).length > 0) {
      hierarchicalNode.properties = properties;
    }

    // 查找当前节点的所有子节点 (即以当前节点为源的边)。
    // 注意：React Flow 的边可能没有明确的顺序，这里按 target ID 排序以确保导出的 JSON 结构稳定。
    const childrenEdges = edges
      .filter((edge) => edge.source === nodeId)
      .sort((a, b) => a.target.localeCompare(b.target)); // 按 target ID 排序，保证导出顺序一致性

    // 如果存在子节点，则递归构建子节点并添加到 children 数组。
    if (childrenEdges.length > 0) {
      hierarchicalNode.children = childrenEdges.map((edge) => buildHierarchy(edge.target));
    }

    return hierarchicalNode;
  };

  // 从根节点开始构建层级结构。
  return buildHierarchy(rootNode.id);
}

/**
 * @function importFromHierarchyJSON
 * @description 将层级化的行为树 JSON 对象转换为 React Flow 使用的扁平化节点和边数组。
 *              此函数用于从外部 JSON 文件导入行为树结构，并将其转换为React Flow 可渲染的格式，
 *              同时自动计算节点的初始布局位置。
 * @param {HierarchicalNode} json - 层级化的行为树 JSON 对象，通常是从 `exportToHierarchyJSON` 函数导出或外部定义的。
 * @returns {{ nodes: BehaviorTreeNode[], edges: Edge[] }} 包含 React Flow 节点和边的数据。
 *          其中 `nodes` 数组的每个节点都包含其在画布上的 `position` 信息，`edges` 数组包含连接信息。
 *          同时自动计算节点的初始布局位置。
 * @param {HierarchicalNode} json - 层级化的行为树 JSON 对象。
 * @returns {{ nodes: BehaviorTreeNode[], edges: Edge[] }} 包含 React Flow 节点和边的数据。
 */
export function importFromHierarchyJSON(json: HierarchicalNode): { nodes: BehaviorTreeNode[]; edges: Edge[] } {
  const nodes: BehaviorTreeNode[] = [];
  const edges: Edge[] = [];
  // 定义节点布局的间距和节点宽度，这些值可以根据实际UI效果进行调整。
  const horizontalGap = 250; // 节点水平间距，确保节点之间有足够的空白。
  const verticalGap = 150; // 节点垂直间距，确保不同层级节点有足够的空间，避免重叠。
  const nodeWidth = 170; // 假设节点的平均宽度，用于在自动布局中计算水平偏移和居中。

  // 用于存储每层节点的引用，以便后续计算总宽度和进行布局。
  // key: level (层级深度, 根节点为0), value: HierarchicalNode[] (该层级的节点数组)。
  const levelNodes: Record<number, HierarchicalNode[]> = {};

  /**
   * @function collectNodesAndEdges
   * @description 递归函数，用于扁平化层级化的节点结构，生成 React Flow 节点和边，并收集每层节点信息。
   *              此函数遍历整个行为树的层级结构，将每个层级化节点转换为 React Flow 节点，并创建相应的边。
   * @param {HierarchicalNode} node - 当前正在处理的层级化节点。
   * @param {string | null} parentId - 当前节点的父节点 ID。如果为根节点，则为 `null`。
   * @param {number} level - 当前节点的层级深度 (根节点为 0)。
   */
  const collectNodesAndEdges = (node: HierarchicalNode, parentId: string | null, level: number) => {
    // 将当前节点添加到对应层级的列表中，以便后续的布局计算。
    if (!levelNodes[level]) {
      levelNodes[level] = [];
    }
    levelNodes[level].push(node);

    // 构建 React Flow 节点。初始位置暂时设置为 { x: 0, y: 0 }，位置将在后续统一计算和更新。
    const newNode: BehaviorTreeNode = {
      id: node.id,
      type: (node.type as NodeType) || NodeType.Action, // 默认类型，如果JSON中未指定，则默认为 Action 节点。
      position: { x: 0, y: 0 }, // 初始位置，将在后续计算中更新。
      data: {
        label: node.name, // 使用 JSON 中的 name 作为节点的显示标签。
        ...node.properties, // 将 properties 对象展开并合并到 data 中，作为节点的自定义数据。
      },
    };
    nodes.push(newNode);

    // 如果存在父节点，则创建一条连接父子节点的边。
    if (parentId) {
      edges.push({
        id: `e${parentId}-${node.id}`, // 边的唯一 ID。
        source: parentId, // 边的起点（父节点 ID）。
        target: node.id, // 边的终点（当前节点 ID）。
        type: 'smoothstep', // 默认边类型，可根据需要调整为 'default', 'straight' 等。
        animated: false, // 边是否带有动画效果。
      });
    }

    // 递归处理当前节点的子节点。
    if (node.children && node.children.length > 0) {
      // 对子节点进行排序，以确保导入时的布局顺序一致性 (例如，按ID排序)，这有助于生成可预测的布局。
      const sortedChildren = [...node.children].sort((a, b) => a.id.localeCompare(b.id));
      sortedChildren.forEach((child) => {
        collectNodesAndEdges(child, node.id, level + 1); // 递归调用，层级深度加1。
      });
    }
  };

  // 首先递归遍历 JSON，收集所有节点和边，并填充 levelNodes 对象。
  collectNodesAndEdges(json, null, 0);

  // 其次，根据收集到的 levelNodes 信息计算每个节点的精确位置。
  // 这里的布局算法尝试将同层级的节点水平居中，并保持一定的水平和垂直间距。
  nodes.forEach((node) => {
    // 找到当前节点所在的层级和其在该层级中的索引。
    let nodeLevel = 0;
    let childIndexInLevel = 0;

    for (const level in levelNodes) {
      const index = levelNodes[level].findIndex((n) => n.id === node.id);
      if (index !== -1) {
        nodeLevel = parseInt(level, 10);
        childIndexInLevel = index;
        break;
      }
    }

    const totalNodesInLevel = levelNodes[nodeLevel]?.length || 1; // 确保totalNodesInLevel不为0，避免除零错误。

    // 计算当前层级的总内容宽度，包括节点宽度和节点之间的水平间距。
    const levelContentWidth = totalNodesInLevel * nodeWidth + (totalNodesInLevel - 1) * horizontalGap;
    // 计算当前层级的起始 X 位置，以实现节点在该层级的水平居中。
    const startX = -levelContentWidth / 2 + nodeWidth / 2;

    // 设置节点的最终位置。
    node.position.x = startX + childIndexInLevel * (nodeWidth + horizontalGap); // 水平位置。
    node.position.y = nodeLevel * (100 + verticalGap); // 垂直位置，基于层级深度和垂直间距。
  });

  return { nodes: nodes, edges: edges };
}